package com.px.lovepath.utils.session;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.impl.client.DefaultHttpClient;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;

import com.px.lovepath.LovePathEnv;
import com.px.lovepath.utils.cache.FileCache;
import com.px.lovepath.utils.cache.HttpCache;
import com.px.lovepath.utils.network.HttpClientManager;
import com.px.lovepath.utils.network.HttpHeaders;
import com.px.lovepath.utils.network.HttpHelper;
import com.px.lovepath.utils.network.NetworkListener;
import com.px.lovepath.utils.network.NetworkMonitor;

public abstract class WebSession implements NetworkListener {
	// 标志会话是否已经关闭
	private boolean mIsClosed = true;
	// 会话状态
	private SessionState mSessionState = SessionState.UNFINISHED;
	// 重试次数
	private int mRetryCount = 0;
	// 最大重试次数
	private int mMaxRetryCount = 0;
	// 会话异常
	private Exception mSessionException = null;
	// 会话任务
	private SessionTask mScheduledSessionTask = null;
	// 会话安排
	private ScheduledFuture<?> mScheduledSessionFuture = null;
	// 会话执行器
	private final ScheduledExecutorService mScheduledSessionExecutor;
	// 会话中最后执行的HTTP请求
	private HttpUriRequest mLastHttpRequest = null;
	// 会话中最后执行的HTTP请求响应
	private HttpResponse mLastHttpResponse = null;
	// 会话使用的HTTP客户端
	private final DefaultHttpClient mHttpClient;
	// 会话消息处理结束信号
	private final Semaphore mSessionMsgHandled = new Semaphore(0);
	// 默认会话使用的HTTP客户端
	private static final DefaultHttpClient mDefaultHttpClient = HttpClientManager.get().getDefaultHttpClient();
	// 默认会话使用的执行器
	private static final ScheduledExecutorService mDefaultSessionExecutor = Executors.newSingleThreadScheduledExecutor();
	// 会话缓存
	private static final HttpCache mHttpCache = new HttpCache("http", 2, new FileCache("http", 400, new File(LovePathEnv.get().getCacheDirectory(), "Http")));
	// 缓存策略
	private CacheStrategy mCacheStrategy = CacheStrategy.DISABLE_CACHE;
	// 默认用户代理
	private String mDefaultUserAgent = null;

	// ### 内嵌类 ###
	// 会话状态
	public static enum SessionState {
		UNFINISHED,
		SUCCEEDED,
		CANCELLED,
		FAILED,
	}
	// 缓存策略
	public static enum CacheStrategy {
		// 禁用缓存
		DISABLE_CACHE,
		// 不使用缓存, 但可以缓存请求结果.
		DO_NOT_USE_CACHE,
		// 仅使用未过期的缓存, 缓存不存在时会发起请求.
		USE_CACHE_IF_FRESH,
		// 使用任何存在的缓存, 缓存不存在时会发起请求. 
		USE_CACHE_IF_EXISTS,
		// 仅使用缓存, 缓存不存在直接返回.
		USE_CACHE_ONLY
	}
	// 会话任务
	private class SessionTask implements Runnable {
		public boolean mIsCancelled = false;
		public long mTaskThreadId = 0;
		public final CacheStrategy mTaskCacheStrategy;
		
		public SessionTask(CacheStrategy cacheStrategy) {
			mTaskCacheStrategy = cacheStrategy;
		}
		@Override
		public void run() {
			mTaskThreadId = Thread.currentThread().getId();
			
			// 开始会话
			Handler prevTryHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
				@Override
				public boolean handleMessage(Message msg) {
					if (mIsClosed) {
						mIsClosed = false;
						mRetryCount = 0;
						mSessionException = null;
						mSessionState = SessionState.UNFINISHED;
						mCacheStrategy = mTaskCacheStrategy;
						NetworkMonitor.get().addNetworkListener(WebSession.this);
						onSessionOpen();
					}
					if (mSessionException != null) {
						++mRetryCount;
						mSessionException = null;
					}

					mSessionMsgHandled.release();
					return true;
				}
			});
			Message prevTryMsg = prevTryHandler.obtainMessage(0);
			prevTryHandler.sendMessage(prevTryMsg);
			mSessionMsgHandled.acquireUninterruptibly();
			
			// 尝试会话
			try {
				onSessionTry();
			} catch (Exception e) {
				e.printStackTrace();
				mSessionException = e;
			} finally {
				closeLastExecution();
			}
			
			// 结束会话
			Handler postTryHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
				@Override
				public boolean handleMessage(Message msg) {
					if (mIsCancelled == false) {
						// 检查会话是否成功
						if (mSessionException != null) {
							boolean shouldRetry = onSessionException(mSessionException, mRetryCount);
							
							// 检查会话是否需要重试
							if (shouldRetry) {
								scheduleSessionTask(mTaskCacheStrategy, mRetryCount * 2 * 1000);
								mSessionMsgHandled.release();
								return true;
							}
							
							// 会话失败
							mSessionState = SessionState.FAILED;
							
						} else {
							
							// 会话成功
							mSessionState = SessionState.SUCCEEDED;
							
						}
					} else {
						// 会话取消
						mSessionState = SessionState.CANCELLED;
						
					}
						
					if (mSessionState == SessionState.SUCCEEDED)
						onSessionSucceeded();
					else if (mSessionState == SessionState.CANCELLED)
						onSessionCancelled();
					else if (mSessionState == SessionState.FAILED)
						onSessionFailed();
					else
						assert false;
					
					if (mScheduledSessionTask == SessionTask.this)
						mScheduledSessionTask = null; // 无后续会话任务
					mIsClosed = true;
					NetworkMonitor.get().removeNetworkListener(WebSession.this);
					onSessionClosed();

					mSessionMsgHandled.release();
					return true;
				}
			});
			Message postTryMsg = postTryHandler.obtainMessage(0);
			postTryHandler.sendMessage(postTryMsg);
			mSessionMsgHandled.acquireUninterruptibly();
		}
	}
	
	// ### 构造函数 ###
	public WebSession() {
		mScheduledSessionExecutor = mDefaultSessionExecutor;
		mHttpClient = mDefaultHttpClient;
	}
	protected WebSession(ScheduledExecutorService sessionExecutor, DefaultHttpClient httpClient) {
		assert httpClient != null;
		mScheduledSessionExecutor = sessionExecutor;
		mHttpClient = httpClient;
	}

	// ### 属性 ###
	public boolean getIsClosed() {
		checkAccess();
		return mIsClosed;
	}
	public void setMaxRetryCount(int maxRetryCount) {
		checkAccess();
		mMaxRetryCount = maxRetryCount;
	}
	public SessionState getSessionState() {
		checkAccess();
		return mSessionState;
	}
	public String getDefaultUserAgent() {
		return mDefaultUserAgent;
	}
	public void setDefaultUserAgent(String userAgent) {
		mDefaultUserAgent = userAgent;
	}
	protected CacheStrategy getCacheStrategy() {
		return mCacheStrategy;
	}

	// ### 方法 ###
	public void refresh() {
		assert checkAccess();

		close();
		open(CacheStrategy.DO_NOT_USE_CACHE);
	}
	public void open() {
		open(CacheStrategy.DISABLE_CACHE);
	}
	public void open(CacheStrategy cacheStrategy) {
		assert checkAccess();

		if (mScheduledSessionTask != null && mScheduledSessionTask.mIsCancelled == false)
			return;
		
		scheduleSessionTask(cacheStrategy, 0);
	}
	public void close() {
		assert checkAccess();

		if (mScheduledSessionTask == null || mScheduledSessionTask.mIsCancelled)
			return;
		
		assert mScheduledSessionFuture != null;
		mScheduledSessionTask.mIsCancelled = true;
		mScheduledSessionFuture.cancel(false);

		try {
			cancelExecution();
		} catch (Exception e) {
			
		}
	}
	
	// ### WebService调用方法 ###
	protected HttpResponse execute(HttpUriRequest httpRequest) throws Exception {
		assert mScheduledSessionTask != null;
		assert mScheduledSessionTask.mTaskThreadId == Thread.currentThread().getId();
		assert httpRequest != null;
		
		if (mIsClosed) throw new WebSessionException();
		
		mLastHttpRequest = httpRequest;
		if (mDefaultUserAgent != null && httpRequest.containsHeader(HttpHeaders.USER_AGENT) == false)
			httpRequest.addHeader(HttpHeaders.USER_AGENT, mDefaultUserAgent);
		
		// 检查是否有缓存的响应
		HttpResponse cachedResponse = null;
		if (mCacheStrategy == CacheStrategy.USE_CACHE_IF_FRESH 
				|| mCacheStrategy == CacheStrategy.USE_CACHE_IF_EXISTS 
				|| mCacheStrategy == CacheStrategy.USE_CACHE_ONLY) {
			HttpCache.CacheSlot<HttpResponse> cacheSlot = mHttpCache.aquireCachedSlot(new HttpCache.CacheKey(httpRequest));
			if (cacheSlot != null) {
				cachedResponse = cacheSlot.getValue();
				mHttpCache.releaseSlot(cacheSlot);
				assert cachedResponse != null;
			}
		}
		
		// 发起请求
		if (cachedResponse != null) {
			if (mCacheStrategy == CacheStrategy.USE_CACHE_IF_EXISTS
					|| mCacheStrategy == CacheStrategy.USE_CACHE_ONLY) {
				// 直接使用缓存的响应
				mLastHttpResponse = cachedResponse;
			} else if (mCacheStrategy == CacheStrategy.USE_CACHE_IF_FRESH) {
				// 检查缓存的响应是否过期
				if (cachedResponse.containsHeader(HttpHeaders.ETAG)) {
					httpRequest.addHeader(HttpHeaders.IF_NONE_MATCH, cachedResponse.getFirstHeader(HttpHeaders.ETAG).getValue());
				}
				if (cachedResponse.containsHeader(HttpHeaders.LAST_MODIFIED)) {
					httpRequest.addHeader(HttpHeaders.IF_MODIFIED_SINCE, cachedResponse.getFirstHeader(HttpHeaders.LAST_MODIFIED).getValue());
				}

				try {
					mLastHttpResponse = mHttpClient.execute(httpRequest);
					StatusLine statusLine = mLastHttpResponse.getStatusLine();
					if (statusLine != null && statusLine.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
						mLastHttpResponse = cachedResponse;
					}
				} catch (IOException e) {
					mLastHttpResponse = cachedResponse;
				}
			} else {
				assert false;
			}
			
			assert mLastHttpResponse != null;
		} else {
			if (mCacheStrategy != CacheStrategy.USE_CACHE_ONLY) {
				mLastHttpResponse = mHttpClient.execute(httpRequest);
			}
		}
		
		if (mLastHttpRequest == null)
			return null;
		
		// 缓存响应
		if (mCacheStrategy != CacheStrategy.DISABLE_CACHE && mLastHttpResponse != cachedResponse) {
			HttpEntity entity = mLastHttpResponse.getEntity();
			if (entity != null) {
				BufferedHttpEntity bufferedEntity = new BufferedHttpEntity(entity);
				mLastHttpResponse.setEntity(bufferedEntity);
			}
			
			HttpCache.CacheSlot<HttpResponse> cacheSlot = mHttpCache.aquireNewSlot(new HttpCache.CacheKey(httpRequest), mLastHttpResponse);
			if (cacheSlot != null) {
				mHttpCache.releaseSlot(cacheSlot);
			}
		}

		if (mIsClosed) {
			cancelExecution();
			throw new WebSessionException();
		}
		
		if (mLastHttpResponse == null)
			throw new WebSessionException();
		
		return mLastHttpResponse;
	}
	protected void publishProgress() {
		assert mScheduledSessionTask != null;
		assert mScheduledSessionTask.mTaskThreadId == Thread.currentThread().getId();

		Handler postHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
			@Override
			public boolean handleMessage(Message msg) {
				onSessionProgressUpdate();
				mSessionMsgHandled.release();
				return true;
			}
		});
		Message postMsg = postHandler.obtainMessage(0);
		postHandler.sendMessage(postMsg);
		mSessionMsgHandled.acquireUninterruptibly();
	}
	protected boolean shouldBreak() {
		assert mScheduledSessionTask != null;
		assert mScheduledSessionTask.mTaskThreadId == Thread.currentThread().getId();

		return mScheduledSessionTask.mIsCancelled;
	}
	
	// ### 可重写方法 ###
	protected void onSessionOpen() {
		
	}
	protected void onSessionCancelled() {
		
	}
	protected void onSessionClosed() {
		
	}
	protected boolean onSessionException(Exception e, int retryCount) {
		if (retryCount < mMaxRetryCount)
			return true;
		else
			return false;
	}
	protected void onSessionProgressUpdate() {
		
	}
	
	// ### 抽象方法 ###
	protected abstract void onSessionTry() throws Exception;
	protected abstract void onSessionSucceeded();
	protected abstract void onSessionFailed();
	
	// ### NetworkListener方法重写 ###
	@Override
	public void onNetworkConnected(NetworkMonitor networkMonitor) {
		if (mIsClosed == false) {
			refresh();
		}
	}
	@Override
	public void onNetworkDisconnected(NetworkMonitor networkMonitor) {

	}

	// ### 实现函数 ###
	private void cancelExecution() throws IllegalStateException, IOException {
		if (mLastHttpRequest != null) {
			mLastHttpRequest.abort();
		}
		
		closeLastExecution();
	}
	private void closeLastExecution() {
		if (mLastHttpRequest != null) {
			try {
				mLastHttpRequest.abort();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		HttpHelper.consumeResponse(mLastHttpResponse);
	}
	private void scheduleSessionTask(CacheStrategy cacheStrategy, long delay) {
		mScheduledSessionTask = new SessionTask(cacheStrategy);
		mScheduledSessionFuture = mScheduledSessionExecutor.schedule(mScheduledSessionTask, delay, TimeUnit.MILLISECONDS);
	}
	private boolean checkAccess() {
		return Thread.currentThread() == Looper.getMainLooper().getThread();
	}
}
